using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using JMClient.Models.Common;
using JMClient.Models.Data;
using JMClient.Models.PhysicalComponents;
using Serilog;
using Serilog.Extensions.Logging;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace JMClient.Service;


/// 日志管理器 - 负责日志实例的注册、管理和生命周期控制
/// 
/// 主要职责：
/// 1. 管理已注册的日志实例（LoggerPools）
/// 2. 提供日志实例的查找和获取服务
/// 3. 提供泛型日志创建服务
/// 4. 管理日志实例的生命周期
/// 
/// 使用示例：
/// // 注册日志实例
/// var systemLog = LogManagement.RegisterLogInstance(typeof(SystemLog));
/// var errorLog = LogManagement.RegisterLogInstance(typeof(ErrorLog));
/// 
/// // 获取泛型日志
/// var systemLogger = LogManagement.GetLogger<UserService>(typeof(SystemLog));
/// var errorLogger = LogManagement.GetLogger<ExceptionHandler>(typeof(ErrorLog));
/// 
/// // 每个 LogInstance 都有自己独立的配置和文件位置
public static class LogManagement
{
    /// <summary>
    /// 日志实例池 - 存储所有已注册的日志实例
    /// Key: 日志实例名称 (ILogInstance.Name)
    /// Value: 日志实例对象
    /// </summary>
    public static ConcurrentDictionary<string, ILogInstance> LoggerPools { get; private set; } = new ConcurrentDictionary<string, ILogInstance>();

    /// <summary>
    /// 日志实例类型映射 - 用于快速查找已注册的日志实例类型
    /// Key: 日志实例类型 (Type)
    /// Value: 日志实例对象
    /// </summary>
    private static ConcurrentDictionary<Type, ILogInstance> _instanceTypeMap = new ConcurrentDictionary<Type, ILogInstance>(); 
    /// <summary>
    /// 初始化日志管理器
    /// </summary>
    public static void Init()
    {
        // 清理现有实例
        LoggerPools.Clear();
        _instanceTypeMap.Clear();
    }
    /// <summary>
    /// 根据名称获取已注册的日志实例
    /// </summary>
    /// <param name="name">日志实例名称</param>
    /// <param name="logInstance">输出参数，返回日志实例</param>
    /// <returns>是否获取成功</returns>
    public static bool GetLogInstance(string name, out ILogInstance logInstance)
    {
        return LoggerPools.TryGetValue(name, out logInstance);
    }

    /// <summary>
    /// 根据类型获取已注册的日志实例
    /// </summary>
    /// <param name="logInstanceType">日志实例类型</param>
    /// <param name="logInstance">输出参数，返回日志实例</param>
    /// <returns>是否获取成功</returns>
    public static bool GetLogInstance(Type logInstanceType, out ILogInstance logInstance)
    {
        return _instanceTypeMap.TryGetValue(logInstanceType, out logInstance);
    }
    /// <summary>
    /// 注册日志实例到管理器
    /// </summary>
    /// <param name="logInstanceType">日志实例类型</param>
    /// <returns>注册的日志实例</returns>
    public static ILogInstance RegisterLogInstance(Type logInstanceType)
    {
        // 参数验证
        if (logInstanceType == null)
            throw new ArgumentNullException(nameof(logInstanceType));
        
        if (!typeof(ILogInstance).IsAssignableFrom(logInstanceType))
            throw new ArgumentException($"类型 {logInstanceType.Name} 必须实现 ILogInstance 接口", nameof(logInstanceType));

        // 1. 检查是否已注册
        if (_instanceTypeMap.TryGetValue(logInstanceType, out var existingInstance))
        {
            return existingInstance;
        }

        try
        {
            // 2. 通过反射实例化具体日志类
            var logInstance = (ILogInstance)Activator.CreateInstance(logInstanceType);
            if (logInstance == null)
                throw new InvalidOperationException($"无法创建 {logInstanceType.Name} 的实例");
            
                    // 3. 调用具体日志类的Create方法，初始化日志工厂
        logInstance.Create(out var loggerFactory);
        
        // 4. 将日志工厂创建的Logger注入到日志实例
            logInstance.Logger = loggerFactory.CreateLogger(logInstanceType);
            
            // 5. 注册到管理器
            if (!LoggerPools.TryAdd(logInstance.Name, logInstance))
                throw new InvalidOperationException($"日志实例名称 '{logInstance.Name}' 已存在");
            
            if (!_instanceTypeMap.TryAdd(logInstanceType, logInstance))
            {
                // 回滚操作
                LoggerPools.TryRemove(logInstance.Name, out _);
                throw new InvalidOperationException($"日志实例类型 {logInstanceType.Name} 注册失败");
            }
            
            return logInstance;
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"注册日志实例 {logInstanceType.Name} 时发生错误", ex);
        }
    }
 /// <summary>
 /// 获取指定类型的泛型日志记录器
 /// </summary>
 /// <param name="logInstanceType"></param>
 /// <typeparam name="T"></typeparam>
 /// <returns></returns>
    public static ILogger<T> GetLogger<T>(Type logInstanceType)
    {
        // 1. 检查是否已注册该类型的日志实例
        if (!_instanceTypeMap.TryGetValue(logInstanceType, out var existingInstance))
        {
            // 2. 如果未注册，先注册日志实例
            existingInstance = RegisterLogInstance(logInstanceType);
        }
        
        // 3. 检查该实例的 Loggers 字典中是否已有 T 类型的 logger
        if (existingInstance.Loggers.TryGetValue(typeof(T), out var existingLogger))
        {
            // 4. 如果已存在，直接返回
            return (ILogger<T>)existingLogger;
        }
        
        // 5. 如果不存在，创建新的 ILogger<T> 并存储到字典中
        var newLogger = existingInstance.LogFactory.CreateLogger<T>();
        existingInstance.Loggers[typeof(T)] = newLogger;
        
        return newLogger;
    }

    /// <summary>
    /// 注销日志实例
    /// </summary>
    /// <param name="logInstanceType">日志实例类型</param>
    /// <returns>是否注销成功</returns>
    public static bool UnregisterLogInstance(Type logInstanceType)
    {
        if (_instanceTypeMap.TryRemove(logInstanceType, out var instance))
        {
            LoggerPools.TryRemove(instance.Name, out _);
            
            // 释放日志工厂资源
            if (instance.LogFactory is IDisposable disposable)
            {
                disposable.Dispose();
            }
            
            return true;
        }
        return false;
    }

    /// <summary>
    /// 获取所有已注册的日志实例名称
    /// </summary>
    /// <returns>日志实例名称列表</returns>
    public static IEnumerable<string> GetRegisteredInstanceNames()
    {
        return LoggerPools.Keys;
    }

    /// <summary>
    /// 获取所有已注册的日志实例类型
    /// </summary>
    /// <returns>日志实例类型列表</returns>
    public static IEnumerable<Type> GetRegisteredInstanceTypes()
    {
        return _instanceTypeMap.Keys;
    }
}

/// <summary>
/// 日志实例接口，定义日志实例的基本行为
/// </summary>
public interface ILogInstance
{
    /// <summary>
    /// 文档形式的文件目录地址
    /// </summary>
    string FilePath{ get; set; }
    /// <summary>
    /// 数据库连接字符
    /// </summary>
    string DbConnectionStringSQlite{ get; set; }
    /// <summary>
    /// 名称
    /// </summary>
    string Name { get; set; }
    /// <summary>
    /// 日志描述
    /// </summary>
    string Description { get; set; }
    /// <summary>
    /// 具体构造方法
    /// </summary>
    /// <param name="loggerFactory"></param>
    /// <returns>ILogger</returns>
    object Create(out ILoggerFactory loggerFactory);
    /// <summary>
    /// 日志实例对象
    /// </summary>
    ILogger Logger { get; set; }
    /// <summary>
    /// 泛型日志对象字典，存储不同类型的 ILogger<T>
    /// </summary>
    ConcurrentDictionary<Type, object> Loggers { get; set; }
    /// <summary>
    /// 该日志实例专用的日志工厂
    /// </summary>
    ILoggerFactory LogFactory { get; set; }
}
/// <summary>
/// 日志实例：系统日志（SystemLog），负责日志写入文件和数据库，支持异步队列批量写入，配置灵活。
/// </summary>
public class SystemLog : ILogInstance
{
    /// <summary>
    /// 日志文件夹路径，默认bin/log
    /// </summary>
    public string FilePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "log");

    /// <summary>
    /// 日志记录器，由工厂注入
    /// </summary>
    public ILogger? Logger { get; set; }

    public ConcurrentDictionary<Type, object> Loggers { get; set; } = new ConcurrentDictionary<Type, object>();
    
    /// <summary>
    /// 该日志实例专用的日志工厂
    /// </summary>
    public ILoggerFactory LogFactory { get; set; }

    /// <summary>
    /// 数据库连接字符串，默认bin/JMData.db
    /// </summary>
    public string DbConnectionStringSQlite { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "JMData.db");

    /// <summary>
    /// 日志实例名称，也是日志文件名前缀，创建时自动带时间
    /// </summary>
    public string Name { get; set; } = "SYSTEM_LOG";

    /// <summary>
    /// 日志实例描述
    /// </summary>
    public string Description { get; set; } = "记录系统运行信息的日志";

    // 日志队列，异步批量写入数据库
    private static readonly ConcurrentQueue<(string Message, LogLevel Level, DateTime Time)> _logQueue = new();
    private static bool _isProcessing = false;
    private static readonly object _processLock = new();
    private static bool _dbInitialized = false;

    /// <summary>
    /// 构造函数，初始化数据库（只做一次）
    /// </summary>
    public SystemLog()
    {
        EnsureDbInitialized();
    }

    /// <summary>
    /// 数据库初始化，建表和预热查询只做一次，提升后续写入性能
    /// </summary>
    private void EnsureDbInitialized()
    {
        if (_dbInitialized) return;
        lock (_processLock)
        {
            if (_dbInitialized) return;
            using (var context = new DeviceDbContext())
            {
                context.Database.EnsureCreated(); // 只做一次
                // 预热查询，避免首次写入慢
                var _ = context.Devices.FirstOrDefault();
                var __ = context.LogData.FirstOrDefault();
            }
            _dbInitialized = true;
        }
    }

    /// <summary>
    /// 创建日志实例，配置Serilog文件写入，日志名为前缀+日期
    /// </summary>
    /// <param name="loggerFactory">输出：日志工厂</param>
    /// <returns>自身实例</returns>
    public object Create(out ILoggerFactory loggerFactory)
    {
        // 确保日志目录存在
        if (!Directory.Exists(FilePath))
            Directory.CreateDirectory(FilePath);
        // 日志文件名：前缀+日期
        string filePrefix = string.IsNullOrWhiteSpace(Name) ? "log" : Name;
        string fileName = $"{filePrefix}-{{Date}}.txt";
        string logFilePath = Path.Combine(FilePath, fileName);
        // 配置Serilog，写入文件
        var serilogLogger = new LoggerConfiguration()
            .WriteTo.File(
                path: logFilePath,
                outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] | {Level:u3} | {Message:lj}{NewLine}",
                rollingInterval: RollingInterval.Day,
                encoding: System.Text.Encoding.UTF8
            )
            .CreateLogger();
        
        // 创建该实例专用的日志工厂
        this.LogFactory = LoggerFactory.Create(builder =>
        {
            builder.AddSerilog(serilogLogger, dispose: true);
        });
        
        // 输出参数，保持兼容性
        loggerFactory = this.LogFactory;
        return this;
    }

    /// <summary>
    /// 写入日志（文件+队列入库），文件写入用Serilog，数据库异步批量写入
    /// </summary>
    /// <param name="message">日志内容</param>
    /// <param name="level">日志级别</param>
    /// <returns>null</returns>
    public object Write(string message, LogLevel level = LogLevel.Information)
    {
        // 文件写入（同步，Serilog内部可配置为异步）
        Logger?.Log(level, message);
        // 入队，后台批量写入数据库
        //_logQueue.Enqueue((message, level, DateTime.Now));
        //StartQueueProcessor();
        return null;
    }

    /// <summary>
    /// 启动队列处理器，异步批量写入数据库，避免主线程阻塞
    /// </summary>
    private void StartQueueProcessor()
    {
        if (_isProcessing) return;
        lock (_processLock)
        {
            if (_isProcessing) return;
            _isProcessing = true;
            Task.Run(async () =>
            {
                while (!_logQueue.IsEmpty)
                {
                    var logsToWrite = new List<(string Message, LogLevel Level, DateTime Time)>();
                    while (_logQueue.TryDequeue(out var item))
                    {
                        logsToWrite.Add(item);
                        if (logsToWrite.Count >= 10) break; // 批量写入10条
                    }
                    if (logsToWrite.Count > 0)
                    {
                        using (var db = new DeviceDbContext())
                        {
                            foreach (var log in logsToWrite)
                            {
                                db.LogData.Add(new SystemData.LogEntity
                                {
                                    LogType = "System",
                                    Date = log.Time,
                                    LogName = this.Name,
                                    LogDescription = this.Description,
                                    LogContent = log.Message
                                });
                            }
                            db.SaveChanges();
                        }
                    }
                    await Task.Delay(100); // 100ms 间隔，避免CPU占用
                }
                _isProcessing = false;
            });
        }
    }
}

/// <summary>
/// 日志实例：错误日志（ErrorLog），专门记录错误信息，使用不同的文件路径和格式
/// </summary>
public class ErrorLog : ILogInstance
{
    /// <summary>
    /// 错误日志文件夹路径，默认bin/error
    /// </summary>
    public string FilePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "error");

    /// <summary>
    /// 日志记录器，由工厂注入
    /// </summary>
    public ILogger? Logger { get; set; }

    public ConcurrentDictionary<Type, object> Loggers { get; set; } = new ConcurrentDictionary<Type, object>();
    
    /// <summary>
    /// 该日志实例专用的日志工厂
    /// </summary>
    public ILoggerFactory LogFactory { get; set; }

    /// <summary>
    /// 数据库连接字符串，默认bin/JMData.db
    /// </summary>
    public string DbConnectionStringSQlite { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "JMData.db");

    /// <summary>
    /// 日志实例名称
    /// </summary>
    public string Name { get; set; } = "ERROR_LOG";

    /// <summary>
    /// 日志实例描述
    /// </summary>
    public string Description { get; set; } = "记录系统错误信息的日志";

    /// <summary>
    /// 构造函数
    /// </summary>
    public ErrorLog()
    {
        // 错误日志不需要数据库初始化，只写入文件
    }

    /// <summary>
    /// 创建错误日志实例，使用不同的配置
    /// </summary>
    /// <param name="loggerFactory">输出：日志工厂</param>
    /// <returns>自身实例</returns>
    public object Create(out ILoggerFactory loggerFactory)
    {
        // 确保错误日志目录存在
        if (!Directory.Exists(FilePath))
            Directory.CreateDirectory(FilePath);
        
        // 错误日志文件名：前缀+日期
        string filePrefix = string.IsNullOrWhiteSpace(Name) ? "error" : Name;
        string fileName = $"{filePrefix}-{{Date}}.txt";
        string logFilePath = Path.Combine(FilePath, fileName);
        
        // 配置Serilog，使用不同的输出格式（包含更多错误信息）
        var serilogLogger = new LoggerConfiguration()
            .WriteTo.File(
                path: logFilePath,
                outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] | {Level:u3} | {SourceContext} | {Message:lj}{NewLine}{Exception}",
                rollingInterval: RollingInterval.Day,
                encoding: System.Text.Encoding.UTF8,
                restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Warning // 只记录警告及以上级别
            )
            .CreateLogger();
        
        // 创建该实例专用的日志工厂
        this.LogFactory = LoggerFactory.Create(builder =>
        {
            builder.AddSerilog(serilogLogger, dispose: true);
        });
        
        // 输出参数，保持兼容性
        loggerFactory = this.LogFactory;
        return this;
    }

    /// <summary>
    /// 写入错误日志（只写入文件，不入数据库）
    /// </summary>
    /// <param name="message">日志内容</param>
    /// <param name="level">日志级别</param>
    /// <returns>null</returns>
    public object Write(string message, LogLevel level = LogLevel.Warning)
    {
        // 只写入文件，错误日志不入数据库
        Logger?.Log(level, message);
        return null;
    }
}